home *** CD-ROM | disk | FTP | other *** search
- /*** MAIN.C - DSDUMP/DSSNAP main program
- *
- * DSDUMP: Display DoubleSpace Compressed Volume File (CVF) information.
- * DSSNAP: Copy CVF system area to a file.
- *
- * Version 1.00.58 12-Mar-1993
- *
- * Notes:
- * -DSNAP => builds DSSNAP program;
- * if not defined, builds DSDUMP program.
- */
-
- #include <ctype.h>
- #include <dos.h> // get thin DOS INT 21h call interface
- #include <fcntl.h>
- #include <io.h>
- #include <memory.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
-
- #include "cvf.h" // Get CVF format
- #include "drvinfo.h" // IsDoubleSpaceDrive()
-
-
- //*****************************************************************************
- //* CONSTANTS *
- //*****************************************************************************
-
- #define szProduct "DoubleSpace" // product name
-
- #define verMAJOR 0 // Major version number (N.xxx)
- #define verMINOR 58 // Minor version number (x.NN)
-
-
- #define cbPERLINE 16 // Number of hex bytes per output line
- #define cCOLUMNS 2 // Number of columns of hex output per line
-
- #define cbFILEBUFFER (cbPERLINE*128) // Size of file buffer
-
- #define cbOUTPUTLINEMAX 100 // Maximum line length on stdout
-
- #define FR_CURRENT_POS -1 // FileRead iseek value to read from current
- // position in the file.
-
- #define chNOTPRINTABLE '.' // Used for non-printable characters in HEX out
-
- #define chSWITCH '/' // Command line switch character
- #define chSWITCH2 '-' // Alternate Command line switch character
-
- #define chRANGE_START_END '-' // Start,End range separator (/M2-37)
- #define chRANGE_START_COUNT '+' // Start,Length range separator (/C20+3)
-
- #define cbDIR_ENT 32 // Number bytes per DOS directory entry
-
- #define szCVF_ROOT "DBLSPACE" // Base name of CVF file
-
- #define seqBAD 255 // Invalid CVF sequence number
- #define seqMAX 254 // Largest valid CVF sequence number
-
- #define cchMAXFILEPATH 129 // Maximum length of a file path
-
-
- //*****************************************************************************
- //* TYPES *
- //*****************************************************************************
-
- typedef int FILEHANDLE; /* fh */ // File handle
- typedef int RETCODE; /* rc */ // Return code
- typedef int SEQ; /* seq */ // CVF sequence number
- typedef unsigned int UINT; /* ui */ // unsigned int
-
-
- /*** RANGE - Range of a regions specified on command line
- *
- * dwRANGE_BAD - value for iFirst or iLast that indicates an error
- * dwRANGE_DEFAULT - value for iFirst or iLast that indicates no
- * command line value was supplied.
- */
- typedef struct RANGE_t { /* range */
- DWORD iFirst; // First entry to display
- DWORD iLast; // Last entry to display
- } RANGE;
- typedef RANGE *PRANGE; /* prange */
- #define dwRANGE_BAD 0xFFFFFFFF // Bad range value
- #define dwRANGE_DEFAULT 0xFFFFFFFE // Default range value
-
-
- /*** REGION - file region characterization
- *
- * Used to record data on the BitFAT, MDFAT, boot sector, DOS FAT,
- * DOS root directory, and the sector heap.
- *
- * The cbTotal is the reserved amount of space in the file for the
- * region. This is usually a limiting factor on growing the CVF.
- *
- * The cbActive is the length of the region (starting from the
- * front) that is valid for the CVF at its current size.
- */
- typedef struct { /* reg */
- long ibStart; // Byte offset in file of region start
- long cbTotal; // Total length of region in bytes
- long cbActive; // Active area of region, in bytes
- BOOL fDisplay; // TRUE => region is interesting
- char *pszName; // Name of region
- } REGION;
- typedef REGION *PREGION; /* preg */
-
-
- /*** INDEXREGION - index into areg of file regions
- *
- * WARNING: This enumeration must be kept parallel to areg!
- */
- typedef enum { /* ireg */
- iregMDBPB,
- iregBITFAT,
- iregRESERVED1,
- iregMDFAT,
- iregRESERVED2,
- iregDOSBOOT,
- iregRESERVED3,
- iregDOSFAT,
- iregDOSROOTDIR,
- iregRESERVED4,
- iregSECTORHEAP,
- cregMAX, // Count of ireg's
- } INDEXREGION;
-
-
- /*** MEMBERTYPE - Display type for a structure member
- *
- * Used to describe the display format of a structure member.
- *
- * WARNING: The elements of this enumeration must match the array
- * cbFromMT!
- */
- typedef enum { /* mt */
- mtBYTE, // printf("%02x",(WORD)BYTE);
- mtBYTE3, // printf("%02x %02x %02x",(WORD)ab[0],(WORD)ab[1],(WORD)ab[2]);
- mtCHAR, // printf("%c",char);
- mtCHAR8, // printf("%8s",ach[8]);
- mtDWORD, // printf("%08lx",DWORD);
- mtINT1, // printf("%3d",(short)char);
- mtINT2, // printf("%5d",short);
- mtINT4, // printf("%10ld",long);
- mtUINT1, // printf("%3u",(WORD)BYTE);
- mtUINT2, // printf("%5u",WORD);
- mtUINT4, // printf("%10lu",DWORD);
- mtWORD, // printf("%04x",WORD);
- } MEMBERTYPE;
- #define mtHIDE 0x80 // OR with other mtXXXX => do not display
-
-
- /*** MEMBERINFO - Describes a stucture member for formatting purposes
- *
- */
- typedef struct { /* mi */
- MEMBERTYPE mt;
- char *psz; // description
- } MEMBERINFO;
- typedef MEMBERINFO *PMEMBERINFO; /* pmi */
-
-
- /*** GLOBAL - structure for global variables
- *
- * This is to make them obvious in the sources
- *
- */
- typedef struct {
- int argc; // argc parameter passed to main(...)
- char **argv; // argv parameter passed to main(...)
- long cbCVF; // Size of CVF in bytes
- long ibCVFStamp2; // Position of 2nd MD_STAMP in CVF
- char chDrive; // Drive letter of mounted CVF, else 0
- MDBPB mp; // MDBPB
- BOOL fIgnoreSigCheck; // TRUE => Ignore signature check
- #ifdef SNAP
- #else
- RANGE rangeBitFAT; // Display range for BitFAT
- RANGE rangeBitFATValid; // Valid range for BitFAT
- RANGE rangeCVF; // Display range for CVF sectors
- RANGE rangeCVFValid; // Valid range for CVF sectors
- RANGE rangeMDFAT; // Display range for MDFAT
- RANGE rangeMDFATValid; // Valid MDFAT range
- RANGE rangeHeap; // Display range for Sector Heap
- RANGE rangeHeapValid; // Valid range for Sector Heap
- BOOL fShowAddresses; // TRUE => -a specified on command line
- BOOL fShowBitFAT; // TRUE => -b specified on command line
- BOOL fShowCVFSectors; // TRUE => -c specified on command line
- BOOL fShowDOSBoot; // TRUE => -t specified on command line
- BOOL fShowDOSFAT; // TRUE => -f specified on command line
- BOOL fShowDOSRootDir; // TRUE => -r specified on command line
- BOOL fShowFragmentation; // TRUE => -g specified on command line
- BOOL fShowHeader; // TRUE => -h specified on command line
- BOOL fShowHeap; // TRUE => -s specified on command line
- BOOL fShowMDFAT; // TRUE => -m specified on command line
- BOOL fShowVerbose; // TRUE => -v specified on command line
- BYTE ab[cbFILEBUFFER]; // File buffer
- #endif
- char ach[cbOUTPUTLINEMAX]; // Line output buffer
- char achCVFName[cchMAXFILEPATH]; // CVF file name
- #ifdef SNAP
- char achOutFileName[cchMAXFILEPATH]; // snap output file name
- #endif
- } GLOBAL;
-
-
- //*****************************************************************************
- //* VARIABLES *
- //*****************************************************************************
-
- /*** g - Global variables
- *
- */
- GLOBAL g;
-
-
- #ifndef SNAP // -----------------------------------------------------
-
- /*** cbFromMt - Get size of structure member from MEMBERTYPE
- *
- * WARNING: The elements of this array must exactly parallel the
- * MEMBERTYPE enumeration!
- */
- int cbFromMt[] = {
- 1, // mtBYTE
- 3, // mtBYTE3
- 1, // mtCHAR
- 8, // mtCHAR8
- 4, // mtDWORD
- 1, // mtINT1
- 2, // mtINT2
- 4, // mtINT4
- 1, // mtUINT1
- 2, // mtUINT2
- 4, // mtUINT4
- 2, // mtWORD
- };
-
-
- /*** amiMDBPB - Description of MDBPB structure members
- *
- */
- MEMBERINFO amiMDBPB[] = {
- { mtBYTE3 , "jmpBOOT : Jump to bootstrap routine" },
- { mtCHAR8 , "achOEMName[8] : OEM Name" },
- { mtUINT2 , "cbPerSec : Count of bytes per sector" },
- { mtUINT1 , "csecPerClu : Count of sectors per cluster" },
- { mtUINT2 , "csecReserved : Count of reserved sectors" },
- { mtUINT1 , "cFATs : Count of FATs" },
- { mtUINT2 , "cRootDirEntries : Count of root directory entries" },
- { mtUINT2 , "csecTotalWORD : Count of total sectors" },
- { mtBYTE , "bMedia : Media byte" },
- { mtUINT2 , "csecFAT : Count of sectors occupied by the FAT" },
- { mtUINT2 , "csecPerTrack : Count of sectors per track" },
- { mtUINT2 , "cHeads : Count of heads" },
- { mtUINT4 , "csecHidden : Count of hidden sectors" },
- { mtUINT4 , "csecTotalDWORD : Count of total sectors" },
- { mtUINT2 , "secMDFATStart : Logical sector of MDFAT" },
- { mtUINT1 , "nLog2cbPerSec : Log base 2 of cbPerSec" },
- { mtUINT2 , "csecMDReserved : Number of sectors reserved by MD" },
- { mtUINT2 , "secRootDirStart : Logical sector of root directory" },
- { mtUINT2 , "secHeapStart : Logical sector of sector heap" },
- { mtUINT2 , "cluFirstData : DOS/DBLSPACE cluster offset" },
- { mtUINT1 , "cpageBitFAT : Count of 'pages' in the BitFAT" },
- { mtWORD |mtHIDE, "RESERVED1 : Reserved1" },
- { mtUINT1 , "nLog2csecPerClu : Log base 2 of csecPerClu" },
- { mtWORD |mtHIDE, "RESERVED2 : Reserved2" },
- { mtUINT4|mtHIDE, "RESERVED3 : Reserved3" },
- { mtUINT4|mtHIDE, "RESERVED4 : Reserved4" },
- { mtBYTE , "f12BitFAT : 1 => 12-bit FAT, 0 => 16-bit FAT" },
- { mtUINT2 , "cmbCVFMax : Maximum CVF size, in megabytes" },
- };
-
- #endif // ifndef SNAP -------------------------------------------------
-
-
- /*** areg - CVF regions
- *
- * This array is filled in by reading the MDBPB and performing
- * computations with its members.
- *
- * WARNING: This array must be kept parallel to INDEXREGION!
- */
- REGION areg[] = {
- /* iStart, cbTotal, cbActive, fInteresting, pszName */
- /* 1234567890123456 */
- { 0L, 0L, 0L, 1, "MDBPB" },
- { 0L, 0L, 0L, 1, "BitFAT" },
- { 0L, 0L, 0L, 0, "<reserved1>" },
- { 0L, 0L, 0L, 1, "MDFAT" },
- { 0L, 0L, 0L, 0, "<reserved2>" },
- { 0L, 0L, 0L, 1, "Boot Sector" },
- { 0L, 0L, 0L, 0, "<reserved3>" },
- { 0L, 0L, 0L, 1, "DOS FAT" },
- { 0L, 0L, 0L, 1, "Root Directory" },
- { 0L, 0L, 0L, 0, "<reserved4>" },
- { 0L, 0L, 0L, 1, "Sector Heap" },
- };
-
-
- /*** szFromMDFAT - Map MDFAT flag bits to strings for display
- *
- */
- char *szFromMDFAT[] = {
- "F,C", // free, compressed
- "F,U", // free, uncompressed
- "A,C", // allocated, compressed
- "A,U" // allocated, uncompressed
- };
-
- /*** apszSyntax - Syntax help
- *
- */
-
- #define isynSUMMARY 2 // Index of syntax summary line
-
- char *apszSyntax[] = {
- #ifdef SNAP
- "Takes a snapshot of the system area of a DoubleSpace drive.",
- "",
- "DSSNAP [/I] [drive] [/O=file]", // Must be isynSUMMARY'nd line
- "",
- " drive DoubleSpace drive, default is current drive;",
- " An explicit CVF name may also be specified.",
- " /I Ignore DoubleSpace signature check",
- " /O=file File name of snapshot file; default is SNAPSHOT.DS",
- #else // SNAP
- "Displays formatted DoubleSpace drive information.",
- "",
- "DSDUMP [/AFHIRTV] [/BCMS[range]] [drive]", // Must be isynSUMMARY'nd line
- "",
- " drive DoubleSpace drive or CVF; default is current drive.",
- " /A Display Addresses of file regions",
- " /F Display DOS FAT",
- " /G Display BitFAT fragmentation report",
- " /H Display header",
- " /I Ignore DoubleSpace signature check",
- " /R Display DOS root directory",
- " /T Display DOS boot sector",
- " /V Display everything (verbose)",
- " /B Display BitFAT",
- " /C Display CVF sectors",
- " /M Display MDFAT",
- " /S Display Sector Heap",
- " range An optional range of entries to display. Use 'n' to select entry n,",
- " 'n-m' to select entries n through m, and 'n+c' to select c entries",
- " starting at entry n. If omitted, all entries are displayed.",
- " Examples: /M27, /S137-142, /C34+4",
- #endif // SNAP
- };
-
- #ifdef SNAP
-
- char szOutName[] = "SNAPSHOT.DS"; // default out file name
-
- #endif
-
- //*****************************************************************************
- //* FUNCTION PROTOTYPES *
- //*****************************************************************************
-
-
- #ifdef SNAP
-
- void SnapCVF(FILEHANDLE fh);
-
- #else
-
- void CheckRange(PRANGE prangeA, PRANGE prangeB, char *psz);
- void DumpBitFAT(FILEHANDLE fh);
- void DumpCVFSectors(FILEHANDLE fh);
- void DumpDOSBoot(FILEHANDLE fh);
- void DumpDOSFAT(FILEHANDLE fh);
- void DumpDOSRootDir(FILEHANDLE fh);
- void DumpMDBPB(void);
- void DumpMDFAT(FILEHANDLE fh);
- void DumpHex(FILEHANDLE fh, long iseek, long cb);
- void DumpHexLine(long iseek, BYTE *pb);
- void DumpHeap(FILEHANDLE fh);
- void DumpSummary(BOOL fFull);
- void FormatMember(char *pch,void *pv, MEMBERTYPE mt);
- char * FormatPercent(char *psz,DWORD num,DWORD den);
- BOOL GetBitFATBit(FILEHANDLE fh, DWORD i);
- void PrintHeader(char *psz);
- void PrintHeader2(char *psz1, char *psz2);
-
- #endif // ifndef SNAP
-
- BOOL BuildCVFName(char *psz, char ach[], int cb, char *chDrive);
- void CheckSignature(FILEHANDLE fh, long seekpos, char *pszName);
- void ComputeRegions(FILEHANDLE fh);
- void Error(char *pszMsg, char *pszParm);
- RETCODE FileRead(FILEHANDLE fh, long iseek, void *pb, WORD cb);
- BOOL IsDriveSpec(char *psz);
- int cdecl main(int argc, char **argv);
- void ParseArgs(int argc, char **argv);
- DWORD ParseDecimal(char **ppch);
- void ParseRange(PRANGE prange, char **ppch);
- void PrintBanner(void);
- void ShowSyntax(BOOL fFull);
-
-
- //*****************************************************************************
- //* FUNCTIONS *
- //*****************************************************************************
-
- /*** main - entry point
- *
- */
- int cdecl main(int argc, char **argv)
- {
- FILEHANDLE fh;
- RETCODE rc;
-
- // Announce ourselves
- PrintBanner();
-
- #ifdef SNAP
- strcpy(g.achOutFileName,szOutName); // Set default output file name
- #endif
-
- // Get arguments
- ParseArgs(argc,argv); // If error, it exits
-
- // If CVF is mounted, make sure it is fully up-to-date
- if (g.chDrive)
- FlushDrive(g.chDrive - 'A', FDOP_DISK_RESET);
-
- // Open CVF
- rc = _dos_open(g.achCVFName,O_RDONLY,&fh);
- if (rc)
- Error("Could not open %s",g.achCVFName);
-
- // Read MDBPB from file
-
- if (FileRead(fh,0L,&g.mp,sizeof(g.mp))) {
- Error("Could not read MDBPB",NULL);
- }
-
- // Compute CVF Regions
-
- ComputeRegions(fh);
-
- #ifdef SNAP
-
- SnapCVF(fh);
-
- #else // SNAP
-
- // Check ranges for validity, if specified
-
- if (g.fShowBitFAT || g.fShowFragmentation)
- CheckRange(&g.rangeBitFAT,&g.rangeBitFATValid,"BitFAT bit");
-
- if (g.fShowCVFSectors)
- CheckRange(&g.rangeCVF,&g.rangeCVFValid,"CVF sector");
-
- if (g.fShowHeap)
- CheckRange(&g.rangeHeap,&g.rangeHeapValid,"Heap sector");
-
- if (g.fShowMDFAT)
- CheckRange(&g.rangeMDFAT,&g.rangeMDFATValid,"MDFAT entry");
-
- // Do operations indicated by flags
-
- DumpSummary(g.fShowAddresses);
-
- if (g.fShowHeader)
- DumpMDBPB();
-
- if (g.fShowBitFAT || g.fShowFragmentation)
- DumpBitFAT(fh);
-
- if (g.fShowCVFSectors)
- DumpCVFSectors(fh);
-
- if (g.fShowMDFAT)
- DumpMDFAT(fh);
-
- if (g.fShowDOSBoot)
- DumpDOSBoot(fh);
-
- if (g.fShowDOSFAT)
- DumpDOSFAT(fh);
-
- if (g.fShowDOSRootDir)
- DumpDOSRootDir(fh);
-
- if (g.fShowHeap)
- DumpHeap(fh);
-
- #endif // ifdef SNAP
-
- // Close CVF
-
- _dos_close(fh);
-
- // Done
- return 0;
- }
-
-
- #ifndef SNAP // ------------------------------------------------------
-
-
- /*** CheckRange - Check that one range is contained in another
- *
- * Check range for inclusion; set default values, if indicated.
- *
- * Entry
- * prangeA - range to be checked for inclusion
- * prangeB - range that should include rangeA
- * psz - String for error message
- *
- * Exit-Success
- * prangeA is included in prangeB;
- * *prangeA is updated to replace any dwRANGE_DEFAULT values
- * with the corresponding values from prangeB.
- *
- * Exit-Failure
- * prangeA is not completely inside prangeB.
- * Error message printed, and program exits.
- */
- void CheckRange(PRANGE prangeA, PRANGE prangeB, char *psz)
- {
- char ach[cbOUTPUTLINEMAX];
-
- // Change default values to the bounding range
-
- if (prangeA->iFirst == dwRANGE_DEFAULT)
- prangeA->iFirst = prangeB->iFirst;
-
- if (prangeA->iLast == dwRANGE_DEFAULT)
- prangeA->iLast = prangeB->iLast;
-
- // Check for inclusion
-
- if (prangeA->iFirst < prangeB->iFirst) {
- sprintf(ach,"%s %ld is not in valid range %ld..%ld",
- psz,prangeA->iFirst,prangeB->iFirst,prangeB->iLast);
- Error(ach,"");
- }
-
- if (prangeA->iLast > prangeB->iLast) {
- sprintf(ach,"%s %ld is not in valid range %ld..%ld",
- psz,prangeA->iLast,prangeB->iFirst,prangeB->iLast);
- Error(ach,"");
- }
- }
-
-
- /*** DumpSummary - Display summary report
- *
- * Entry
- * fFull - TRUE if regions addresses should be displayed.
- * g.mp - Filled in with MD BPB from CVF already.
- *
- * Exit
- * Summary written to stdout
- */
- void DumpSummary(BOOL fFull)
- {
- int ireg;
- long cbSec; // Count of bytes per sector
-
- printf("\n");
- if (g.chDrive != 0)
- printf("Drive: %c (mounted from %s)\n",g.chDrive,g.achCVFName);
- else
- printf("File: %s\n",g.achCVFName);
-
- // Are we just printing the header?
- if (!fFull) // Yes
- return;
-
- cbSec = g.mp.cbPerSec;
- /* 12345678901234567890123456789012345678901234567890123456789012345678901234567890 */
- printf("Area Start: Sector Length: cSectors Active: cSectors\n");
- printf("-------------- ----------------- ----------------- -----------------\n");
-
- for (ireg=0; ireg<cregMAX; ireg++) {
- printf("%-14s %9ld %7ld %9ld %7ld %9ld %7ld\n",
- areg[ireg].pszName,
- areg[ireg].ibStart, areg[ireg].ibStart/cbSec,
- areg[ireg].cbTotal, areg[ireg].cbTotal/cbSec,
- areg[ireg].cbActive, (areg[ireg].cbActive+cbSec-1)/cbSec);
- // NOTE: Make sure sector count of active area includes last sector
- }
- }
-
-
- /*** DumpMDBPB - Dump the MDBPB
- *
- * Entry
- * g.mp - Filled in with MDBPB from CVF already
- *
- * Exit
- * Formatted MDBPB written to stdout
- */
- void DumpMDBPB(void)
- {
- int i;
- void *pv;
-
- PrintHeader("MDBPB Structure");
-
- // Print out each member of MDBPB structure
-
- pv = &g.mp;
- for (i=0; i<(sizeof(amiMDBPB)/sizeof(MEMBERINFO)); i++) {
- if (!(amiMDBPB[i].mt & mtHIDE)) { // Not a hidden member
- FormatMember(g.ach,pv,amiMDBPB[i].mt); // Create displayable value
- printf("%10s = %s\n",g.ach,amiMDBPB[i].psz); // Display it
- }
- (char *)pv += cbFromMt[amiMDBPB[i].mt]; // Next structure member
- }
- }
-
-
- /*** DumpMDFAT - Dump the MDFAT
- *
- * Note that MDFAT entries do not correspond directly with DOS FAT
- * entries. DBLSPACE.BIN calculates it's MDFAT 'cluster' number by
- * dividing the sector number passed from MS-DOS by the sectors per
- * cluster. For example, with 8k clusters, FAT cluster 2 (the first
- * cluster available for data) might start at sector 192.
- * 192 / 16 (sectors per cluster) = 12, the MDFAT entry number. A DOS
- * FAT entry number can be converted to it's corresponding MDFAT entry
- * number by adding the cluFirstData value to the DOS cluster number.
- * For the above example, DOS cluster 2 + cluFirstData (10) = MDFAT
- * entry 12. The cluFirstData field contains the number of MDFAT
- * entries ('clusters') occupied by the DOS boot record, reserved area,
- * and root directory.
- *
- * Entry
- * fh - file handle of CVF
- * g.mp - filled in the MDBPB from CVF
- * g.rangeMDFAT - range of entries to display
- *
- * Exit
- * Formatted MDFAT written to stdout
- */
- void DumpMDFAT(FILEHANDLE fh)
- {
- char ach[cbOUTPUTLINEMAX];
- UINT clPiece;
- DWORD cur_mdfat;
- DWORD *pMDFAT;
- UINT iLine;
- DWORD mdfat_entry;
-
- sprintf(ach,"MDFAT entries %ld to %ld",
- g.rangeMDFAT.iFirst,g.rangeMDFAT.iLast);
- PrintHeader2(ach,
- "Flags: A=Allocated, F=Free, C=Compressed, U=Uncompressed");
-
- /* 1234567890123456789012345678901234567890 */
- printf("FAT# MDFAT# Flags cUnc cCmp secStart\n");
- printf("----- ------ ----- ---- ---- --------\n");
-
- cur_mdfat = g.rangeMDFAT.iFirst;
-
- // Process one piece at a time (limited by our buffer size)
- while (cur_mdfat <= g.rangeMDFAT.iLast) {
-
- // Count of MDFAT entries to process
- clPiece = (WORD)min(g.rangeMDFAT.iLast-cur_mdfat+1,
- sizeof(g.ab)/cbMDFATENTRY);
-
- // Get a piece of the file to print
- if (FileRead(fh,
- areg[iregMDFAT].ibStart + cbMDFATENTRY*cur_mdfat,
- g.ab,
- sizeof(g.ab))) {
- Error("Read of CVF failed!",NULL);
- }
-
- // Display the piece one line at a time
- pMDFAT = (DWORD *)g.ab;
- for (iLine=0; iLine<clPiece; iLine++) {
- mdfat_entry = pMDFAT[iLine]; // fetch the MDFAT entry
- printf("%5ld %6ld %5s %2d %2d %8ld\n",
- cur_mdfat - g.rangeMDFATValid.iFirst + 2, // FAT number
- cur_mdfat, // MDFAT number
- szFromMDFAT[3 & (mdfat_entry >> 30)], // Flags
- 1+ (int) (15 & (mdfat_entry >> 26)), // Count uncomp
- 1+ (int) (15 & (mdfat_entry >> 22)), // Count comp
- mdfat_entry & 0x3fffff); // Sector #
- cur_mdfat++;
- }
- }
-
- }
-
-
- /*** DumpBitFAT - Dump the BitFAT and/or fragmentation report
- *
- * Entry
- * fh - file handle of CVF
- * g.mp - filled in the MDBPB from CVF
- * g.rangeBitFAT - range of BitFAT entries to display
- * g.fShowBitFAT - TRUE => show BitFAT entries
- * g.fShowFragmentation - TRUE => show fragmentation report
- *
- * Exit
- * Formatted BitFAT and/or fragmentation report written to stdout
- */
- void DumpBitFAT(FILEHANDLE fh)
- {
-
- #define iFREE 0 // Index for counts of free sectors
- #define iUSED 1 // Index for counts of used sectors
-
- DWORD aavgRun16[2]; // Average length of runs >= 16 sectors
- char ach[cbOUTPUTLINEMAX]; // Line output buffer
- char ach1[10]; // Buffer for formatting percentages
- char ach2[10]; // Buffer for formatting percentages
- DWORD acRun[16][2]; // Count of runs[length][free/used]
- DWORD asumRun[2]; // Sum of all sectors [free/used]
- DWORD asumRun16[2]; // Sum of sectors in runs >= 16 sec [f/u]
- DWORD csecRun;
- BOOL fBit;
- DWORD i;
- int j;
- DWORD iRunFirst;
- DWORD iRunLast;
-
- if (g.fShowBitFAT) { // Print header for BitFAT
- sprintf(ach,"BitFAT for heap sectors %ld to %ld",
- g.rangeBitFAT.iFirst,g.rangeBitFAT.iLast);
- PrintHeader(ach);
- }
-
- if (g.fShowFragmentation) { // Zero counters
- for (j=0; j<16; j++) {
- acRun[j][iFREE] = 0;
- acRun[j][iUSED] = 0;
- }
- asumRun16[iFREE] = 0;
- asumRun16[iUSED] = 0;
- }
-
- i = g.rangeBitFAT.iFirst;
- iRunFirst = i;
- fBit = GetBitFATBit(fh,i);
-
- i++;
- while (i <= g.rangeBitFAT.iLast) {
- // Find run of bits with same setting
- while ( (i <= g.rangeBitFAT.iLast) && (fBit == GetBitFATBit(fh,i)) ) {
- i++;
- }
- // Note run, prepare to look for end of next run
- iRunLast = i-1;
- csecRun = iRunLast - iRunFirst + 1;
- if (g.fShowBitFAT) {
- printf("%s %ld to %ld, length %ld\n", fBit ? "USED" : "free",
- iRunFirst, iRunLast, csecRun);
- }
- if (g.fShowFragmentation) { // Accumulate statistics
- if (csecRun < 16) { // Run less than 16 sectors
- acRun[csecRun-1][fBit] += 1;
- }
- else { // Run >= 16 sectors
- acRun[15][fBit] += 1; // Count run
- asumRun16[fBit] += csecRun; // Sum number of sectors
- }
- }
- iRunFirst = i; // New first bit
- fBit = !fBit; // Bit is reversed
- }
-
- if (g.fShowFragmentation) { // Generate report
- PrintHeader("Fragmentation Report");
- printf("\n");
- printf(" free used\n");
- printf("size count count free %% used %%\n");
- printf("----- ----- ----- ------- -------\n");
-
- // Compute average length of runs >= 16 sectors long
- for (j=0; j<2; j++) {
- if (acRun[15][j] > 0)
- aavgRun16[j] = asumRun16[j]/acRun[15][j];
- else
- aavgRun16[j] = 0;
- asumRun[j] = asumRun16[j]; // Sum of all runs, by type
- }
-
- // Sum up 1..15 sector runs
- for (j=0; j<15; j++) {
- asumRun[iFREE] += acRun[j][iFREE]*(j+1);
- asumRun[iUSED] += acRun[j][iUSED]*(j+1);
- }
-
- // Print 1..15 sector runs stats
- for (j=0; j<15; j++) {
- printf("%5d %5ld %5ld %7s %7s\n",
- j+1, acRun[j][iFREE], acRun[j][iUSED],
- FormatPercent(ach1,(j+1)*acRun[j][iFREE],asumRun[iFREE]),
- FormatPercent(ach2,(j+1)*acRun[j][iUSED],asumRun[iUSED])
- );
- }
-
- // Print 16+ sector run stats
- printf("%5ld %5ld %7s Free runs >= 16 sectors\n",
- aavgRun16[iFREE], acRun[15][iFREE],
- FormatPercent(ach1,
- aavgRun16[iFREE]*acRun[15][iFREE],asumRun[iFREE])
- );
- printf("%5ld %5ld %7s Used runs >= 16 sectors\n",
- aavgRun16[iUSED], acRun[15][iUSED],
- FormatPercent(ach2,
- aavgRun16[iUSED]*acRun[15][iUSED],asumRun[iUSED])
- );
- printf("---------------------\n");
- printf("Total Free: %9ld\n", asumRun[iFREE]);
- printf("Total Used: %9ld\n", asumRun[iUSED]);
- printf("---------------------\n");
- printf("TOTAL %9ld\n", asumRun[iFREE]+asumRun[iUSED]);
- }
- }
-
-
- /*** FormatPercentage - Make PSZ from percentage of two dwords
- *
- * Entry
- * psz - Buffer to receive formatted percentage
- * num - Numerator
- * den - Denominator
- *
- * Exit
- * psz has percentage num/den, e.g., " 67.03%"
- * returns psz, for use by caller
- *
- * NOTE: If den is zero, then result is " 0.00%".
- */
- char *FormatPercent(char *psz,DWORD num,DWORD den)
- {
- DWORD dwUnit=0; // nnn.00 portion
- DWORD dwFrac=0; // 000.nn portion
-
- if (den != 0) {
- // NOTE: Num is at most 1024*1024, as that is the maximum number
- // of sectors in a CVF sector heap. Since a DWORD holds
- // numbers up to 4 billion, we won't overflow here.
-
- dwUnit = (100*num)/den;
-
- // NOTE: To get the .00% amount, we have to reduce the magnitude
- // of num (100*100*num would overflow 4 billion in the worst
- // case). So we subtract the units amount before we do the
- // second multiply by 100.
-
- dwFrac = (100*(num*100 - dwUnit*den))/den;
- }
-
- // Now format result
-
- sprintf(psz,"%3ld.%02ld%%",dwUnit,dwFrac);
-
- return psz;
- }
-
-
- /*** GetBitFATBit - Get one bit from the BitFAT
- *
- * Entry
- * fh - file handle of CVF
- * i - index of bit to return (using Sector Heap numbering!)
- * g.mp - filled in the MDBPB from CVF
- *
- * Exit
- * Returns value of BitFAT[i]
- *
- * NOTE: g.ab is used as a BitFAT "page" cache (in this case, we
- * do no use the standard 2K BitFAT page size), so the caller
- * must ensure that g.ab is not modified between calls to
- * this routine!
- */
- BOOL GetBitFATBit(FILEHANDLE fh, DWORD i)
- {
- long ib; // Byte index into BitFAT
- int ibit; // Bit index into BitFAT word
- int iw; // Word index into BitFAT page
- int ipage; // Page index into BitFAT
- static int ipageCached=-1; // Index of cached BitFAT page in g.ab
- WORD *pw=(WORD *)g.ab; // Pointer to BitFAT page
- RETCODE rc;
-
- // Adjust index to be zero based
- i -= g.rangeBitFATValid.iFirst;
-
- // Get byte index of BitFAT word containing the requested bit
- ib = (i>>4)<<1;
-
- // Make sure we have BitFAT page in cache
- ipage = (int)(ib / sizeof(g.ab));
- if (ipage != ipageCached) { // Read page of bitfat
- rc = FileRead(fh,
- areg[iregBITFAT].ibStart + ipage*sizeof(g.ab),
- g.ab,
- sizeof(g.ab)
- );
- if (rc)
- Error("Read failed on CVF BitFAT",NULL);
- ipageCached = ipage; // New cached page
- }
-
- // Now extract the requested bit
- iw = (int)(ib - ipageCached*(long)sizeof(g.ab)) / 2;
- ibit = 15 - ((WORD)i % 16); // 0th bit is at bit 15!
- //BUG printf("iw=%d, pw[iw]=%04x, ibit=%2d\n",iw,pw[iw],ibit);
- if (ibit == 0)
- return pw[iw] & 0x01; // Skip the shift
- else
- return (pw[iw] >> ibit) & 0x01; // Shift and mask
- }
-
-
- /*** DumpCVFSectors - Dump raw sectors from CVF
- *
- * Entry
- * fh - file handle of CVF
- * g.mp - filled in the MDBPB from CVF
- * g.rangeCVF - range of CVF sectors to dump
- *
- * Exit
- * Selected range from CVF displayed
- */
- void DumpCVFSectors(FILEHANDLE fh)
- {
- char ach[cbOUTPUTLINEMAX];
- DWORD iSec;
- DWORD offStart;
-
- sprintf(ach,"CVF Sectors %ld to %ld",
- g.rangeCVF.iFirst,g.rangeCVF.iLast);
- PrintHeader(ach);
-
- for (iSec=g.rangeCVF.iFirst; iSec<=g.rangeCVF.iLast; iSec++) {
- printf("\nCVF sector %ld\n",iSec);
-
- offStart = g.mp.cbPerSec*iSec;
- DumpHex(fh,offStart,g.mp.cbPerSec);
- }
- }
-
-
- /*** DumpDOSBoot - Dump the DOS boot sector
- *
- * Entry
- * fh - file handle of CVF
- * g.mp - filled in the MDBPB from CVF
- *
- * Exit
- * Formatted DOS Boot sector written to stdout
- */
- void DumpDOSBoot(FILEHANDLE fh)
- {
- PrintHeader("DOS Boot Sector");
- // Don't format the info, just dump hex bytes
- DumpHex(fh,areg[iregDOSBOOT].ibStart,256);
- }
-
-
- /*** DumpHeap - Dump the Heap
- *
- * Entry
- * fh - file handle of CVF
- * g.mp - filled in the MDBPB from CVF
- * g.rangeHeap - range of Sector Heap to dump
- *
- * Exit
- * Selected range from sector heap displayed
- */
- void DumpHeap(FILEHANDLE fh)
- {
- char ach[cbOUTPUTLINEMAX];
- DWORD iSec;
- DWORD offStart;
-
- sprintf(ach,"Sector Heap entries %ld to %ld",
- g.rangeHeap.iFirst,g.rangeHeap.iLast);
- PrintHeader(ach);
-
- for (iSec=g.rangeHeap.iFirst; iSec<=g.rangeHeap.iLast; iSec++) {
- printf("\nHeap sector %ld\n",iSec);
-
- // MDFAT sector numbers are 1 less than the CVF sector number
- offStart = g.mp.cbPerSec*(iSec+1);
-
- DumpHex(fh,offStart,g.mp.cbPerSec);
- }
- }
-
-
- /*** DumpDOSFAT - Dump the DOS FAT
- *
- * Entry
- * fh - file handle of CVF
- * g.mp - filled in the MDBPB from CVF
- *
- * Exit
- * Formatted DOS FAT sector written to stdout
- */
- void DumpDOSFAT(FILEHANDLE fh)
- {
- PrintHeader("DOS FAT - just a portion of whole table");
- DumpHex(fh,areg[iregDOSFAT].ibStart,256);
- }
-
-
- /*** DumpDOSRootDir - Dump the DOS Root directory
- *
- * Entry
- * fh - file handle of CVF
- * g.mp - filled in the MDBPB from CVF
- *
- * Exit
- * Formatted DOS root directory written to stdout
- */
- void DumpDOSRootDir(FILEHANDLE fh)
- {
- PrintHeader("DOS Root Directory - just a portion of whole table");
- DumpHex(fh,areg[iregDOSROOTDIR].ibStart,256);
- }
-
-
- /*** DumpHex - Dump a portion of a file as Hex bytes
- *
- * Entry
- * fh - File handle of CVF
- * iseek - File position
- * cb - Count of bytes to dump
- *
- * Exit-Success
- * Formatted information written to stdout
- *
- * Exit-Failure
- * Exits program with error message.
- * NOTE: Some output may have been written to stdout
- */
- void DumpHex(FILEHANDLE fh, long iseek, long cb)
- {
- long cbPiece;
- RETCODE rc;
- int iLine;
- int cLine;
- DWORD iseekPiece;
- BYTE *pb;
-
- iseekPiece = iseek;
-
- // Process one piece at a time (limited by our buffer size)
- while (cb > 0) {
- cbPiece = (WORD)min(cb,sizeof(g.ab)); // Size of piece to process
- cLine = (int)(((long)cbPiece)+cbPERLINE-1)/cbPERLINE; // count of lines
-
- // Get a piece of the file to print
- rc = FileRead(fh,iseekPiece,g.ab,sizeof(g.ab));
- if (rc)
- Error("Read failed on CVF",NULL);
-
- pb = g.ab; // Start of buffer
- // Print the piece one line at a time
- for (iLine=0; iLine<cLine; iLine++) {
- DumpHexLine(iseekPiece,pb); // Print a line
- pb += cbPERLINE; // Advance buffer pointer
- iseekPiece += cbPERLINE; // Advance seek position
- }
- cb -= cbPiece; // Reduce amount left to dump
- }
- }
-
-
- /*** DumpHexLine - Dump one line of hex output to stdout
- *
- * Entry
- * iseek - File position to report
- * pb - Pointer to buffer to dump
- *
- * Exit
- *
- * Sample HEX output
- *
- * 123456789 123456789 123456789 123456789 123456789 123456789 123456789 12345
- * 0000 30 31 32 33 34 35 36 37 38 39 00 00 00 00 00 30 0123456789.....0
- *
- */
- void DumpHexLine(long iseek, BYTE *pb)
- {
- char ch;
- int i;
- int ich;
- char *pbSave;
-
- #define cchHEXFILEOFFSET 10
- #define cchHEXVALUE 3
-
- pbSave = (char *)pb; // Save pointer for ASCII section
-
- // Generate file offset
- sprintf(g.ach,"%8lx ",iseek);
- ich = cchHEXFILEOFFSET;
-
- // Generate hex values
- for (i=0; i<cbPERLINE; ) {
- sprintf(&g.ach[ich],"%02x ",pb[i]);
- ich += cchHEXVALUE;
- i++;
- // Check for column break
- if ( (i % (cbPERLINE/cCOLUMNS)) == 0) {
- g.ach[ich++] = ' '; // Add column separator
- }
- }
-
- g.ach[ich++] = ' '; // Add space before ASCII section
-
- // Generate ASCII section
- for (i=0; i<cbPERLINE; i++) {
- if (isprint(pb[i]))
- ch = pb[i];
- else
- ch = chNOTPRINTABLE;
- g.ach[ich++] = ch;
- }
- g.ach[ich++] = '\0'; // Terminate line
-
- printf("%s\n",g.ach); // Print it
- }
-
- #endif // ifndef SNAP ------------------------------------------------
-
-
- /*** FileRead - read data from file at particular location
- *
- * Entry
- * fh - File handle
- * iseek - Byte offset from start of file
- * 0 = front of file
- * -1 = current file position
- * pb - Buffer to receive data
- * cb - Size of buffer
- *
- * Exit-Success
- * Returns 0
- * pb filled in with requested data
- *
- * Exit-Failure
- * Returns non-zero error code
- */
- RETCODE FileRead(FILEHANDLE fh, long iseek, void *pb, WORD cb)
- {
- WORD cbRead;
- long iseekNew;
- WORD rc;
-
- // Seek to requested location
- if (iseek != FR_CURRENT_POS) {
- iseekNew = lseek(fh,iseek,SEEK_SET);
- if (iseekNew != iseek)
- return errno;
- }
-
- // Read data
- rc = _dos_read(fh,pb,cb,&cbRead);
- if (rc != 0) // Call failed
- return rc;
- if (cbRead != cb) // Did not get enough data
- return 1;
-
- return 0; // Success
- }
-
-
- /*** Error - format error message, display it, and exit
- *
- * Entry
- * pszMsg - error message
- * pszParm - replacable parm for %s in pszMsg
- *
- * Exit
- * message formatted and displayed
- * exit program
- */
- void Error(char *pszMsg, char *pszParm)
- {
- printf("Error: ");
- printf(pszMsg,pszParm);
- exit(1);
- }
-
- #ifndef SNAP // ------------------------------------------------------
-
- /*** FormatMember - Format a value, as indicated by its MEMBERTYPE
- *
- * Entry
- * pch - Buffer to fill in
- * pv - Pointer to value
- * mt - MEMBERTYPE of value
- *
- * Exit
- * pch filled in with formatted value
- */
- void FormatMember(char *pch,void *pv, MEMBERTYPE mt)
- {
- int i;
- WORD w;
- WORD w2;
- WORD w3;
-
- mt = mt & (~mtHIDE); // Mask off hidden attribute
-
- switch (mt) {
-
- case mtBYTE :
- w = (WORD)(*(BYTE *)pv); // WORD from BYTE
- sprintf(pch,"%02x",w);
- break;
-
- case mtBYTE3 :
- w = (WORD)(*((BYTE *)pv+0)); // WORD from BYTE
- w2 = (WORD)(*((BYTE *)pv+1)); // WORD from BYTE
- w3 = (WORD)(*((BYTE *)pv+2)); // WORD from BYTE
- sprintf(pch,"%02x %02x %02x", w, w2, w3);
- break;
-
- case mtCHAR :
- sprintf(pch,"%c",*(char *)pv);
- break;
-
- case mtCHAR8 :
- memcpy(g.ach,(char *)pv,8); // Copy string
- g.ach[8] = '\0'; // Add null terminator
- sprintf(pch,"%s",g.ach);
- break;
-
- case mtDWORD :
- sprintf(pch,"%08lx",*(DWORD *)pv);
- break;
-
- case mtINT1 :
- i = (int)(*(char *)pv); // 2 byte int from 1 byte int
- sprintf(pch,"%3d",i);
- break;
-
- case mtINT2 :
- sprintf(pch,"%5d",*(short *)pv);
- break;
-
- case mtINT4 :
- sprintf(pch,"%10ld",*(long *)pv);
- break;
-
- case mtUINT1 :
- w = (WORD)(*(BYTE *)pv); // WORD from BYTE
- sprintf(pch,"%3u",w);
- break;
-
- case mtUINT2 :
- sprintf(pch,"%5u",*(WORD *)pv);
- break;
-
- case mtUINT4 :
- sprintf(pch,"%10lu",*(DWORD *)pv);
- break;
-
- case mtWORD :
- sprintf(pch,"%04x",*(WORD *)pv);
- break;
-
- default :
- Error("Unexpected MEMBERTYPE",NULL);
-
- }
- }
-
-
- /*** PrintHeader - print header for a report section
- *
- * Entry
- * psz - section name
- *
- * Exit
- * Section name written to stdout with underlines.
- */
- void PrintHeader(char *psz)
- {
- PrintHeader2(psz,NULL);
- }
-
-
- /*** PrintHeader2 - print two line header for a report section
- *
- * Entry
- * psz1 - first line
- * psz2 - second line (ignored if NULL)
- *
- * Exit
- * Lines written to stdout, with underlines after second line.
- */
- void PrintHeader2(char *psz1, char *psz2)
- {
- int cb;
- int cb2;
-
- printf("\n%s\n",psz1); // Print name
-
- cb = strlen(psz1);
-
- // Print 2nd line, and update longest line length
- if (psz2) {
- printf("%s\n",psz2);
- cb2 = strlen(psz2);
- if (cb2 > cb)
- cb = cb2;
- }
-
- // Generate and print underline
- memset(g.ach,'-',cb);
- g.ach[cb] = '\0';
- printf("%s\n",g.ach);
- }
-
-
- #endif // ifndef SNAP ------------------------------------------------
-
- /*** ParseArgs - Parse command-line arguments
- *
- * Entry
- * argc - count of arguments
- * argv - array of arguments
- *
- * Exit-Success
- * GLOBAL structure (g) fields filled in
- *
- * Exit-Failure
- * Prints message and exits program.
- */
- void ParseArgs(int argc, char **argv)
- {
- char ch;
- BOOL fCVFSeen=0; // TRUE if CVF name or drive seen
- BOOL fFlagSeen=0; // TRUE if any flags specified
- BOOL fFlagSeenOld=0; // Value of fFlagSeen on previous loop iteration
- int i;
- char *pch;
-
- // Save command line pointers for later
- g.argc = argc;
- g.argv = argv;
-
- #ifndef SNAP
- // Mark all display ranges as defaults, as no values specified, yet
-
- g.rangeBitFAT.iFirst = dwRANGE_DEFAULT;
- g.rangeBitFAT.iLast = dwRANGE_DEFAULT;
-
- g.rangeCVF.iFirst = dwRANGE_DEFAULT;
- g.rangeCVF.iLast = dwRANGE_DEFAULT;
-
- g.rangeHeap.iFirst = dwRANGE_DEFAULT;
- g.rangeHeap.iLast = dwRANGE_DEFAULT;
-
- g.rangeMDFAT.iFirst = dwRANGE_DEFAULT;
- g.rangeMDFAT.iLast = dwRANGE_DEFAULT;
- #endif
-
- // Parse each argument
- for (i=1; i<argc; i++) {
- if ( (argv[i][0] == chSWITCH) ||
- (argv[i][0] == chSWITCH2) ) {
-
- fFlagSeenOld = fFlagSeen;
- fFlagSeen = 1;
- pch = &argv[i][1]; // Start with first character
- while (*pch) {
- ch = (char)toupper((int)*pch);
-
- switch (ch) {
- case 'I':
- g.fIgnoreSigCheck = 1;
- fFlagSeen = fFlagSeenOld; // No display, so ignore as flag
- break;
- #ifdef SNAP
- case 'O': // output file specified
- pch++;
- if ((*pch == '\0') || (*pch != '=') || (pch[1] == '\0') )
- Error("Bad parameter format: %s",argv[i]);
- else { // Use specified file
- strcpy(g.achOutFileName,pch+1);
- pch[1] = '\0'; // Stop parsing this token
- // NOTE: We set pch[1], because loop does pch++
- // *before* checking for null terminator!
- }
- break;
-
- #else // SNAP
- case 'A': g.fShowAddresses = 1; break;
- case 'F': g.fShowDOSFAT = 1; break;
- case 'G': g.fShowFragmentation = 1; break;
- case 'H': g.fShowHeader = 1; break;
- case 'R': g.fShowDOSRootDir = 1; break;
- case 'T': g.fShowDOSBoot = 1; break;
-
- case 'B':
- g.fShowBitFAT = 1;
- ParseRange(&g.rangeBitFAT,&pch);
- pch--; // Point to last char, for pch++ below
- break;
-
- case 'C':
- g.fShowCVFSectors = 1;
- ParseRange(&g.rangeCVF,&pch);
- pch--; // Point to last char, for pch++ below
- break;
-
- case 'M':
- g.fShowMDFAT = 1;
- ParseRange(&g.rangeMDFAT,&pch);
- pch--; // Point to last char, for pch++ below
- break;
-
- case 'S':
- g.fShowHeap = 1;
- ParseRange(&g.rangeHeap,&pch);
- pch--; // Point to last char, for pch++ below
- break;
-
- case 'V':
- g.fShowVerbose = 1; // Remember verbose
-
- // Set all the other flags, except the sector heap
- // and CVF flags, since the output for those are
- // very, very large.
- g.fShowBitFAT = 1;
- g.fShowDOSBoot = 1;
- g.fShowDOSFAT = 1;
- g.fShowDOSRootDir = 1;
- g.fShowFragmentation = 1;
- g.fShowHeader = 1;
- g.fShowMDFAT = 1;
- break;
- #endif // SNAP
- case '?':
- ShowSyntax(1);
- break;
-
- default:
- g.ach[0] = *pch;
- g.ach[1] = '\0';
- Error("Unknown switch: %s",g.ach);
- }
- pch++;
- }
- }
- else if (fCVFSeen) {
- Error("Too many parameters: %s",argv[i]);
- }
- else { // Must be the drive letter or CVF name
- if (!BuildCVFName(argv[i],g.achCVFName,
- sizeof(g.achCVFName),&g.chDrive))
- Error("%s",g.achCVFName);
- fCVFSeen = 1;
- }
- }
-
- if (!fCVFSeen) // Use default CVF
- if (!BuildCVFName(NULL,g.achCVFName,sizeof(g.achCVFName),&g.chDrive))
- Error("%s",g.achCVFName);
-
- #ifndef SNAP
- // If no flags specifed, show region addresses
- if (!fFlagSeen)
- g.fShowAddresses = 1;
- #endif
- }
-
-
- /*** ParseRange - parse range specification from command line
- *
- * Entry
- * prange - pointer to RANGE to be filled in
- * ppch - pointer to pointer to switch character immediately
- * preceding text to be parsed.
- *
- * Exit-Success
- * Returns filled in prange. Note that if only a first value
- * is specfied (e.g., 2, as opposed to 2-7), then last is set
- * equal to first.
- * *ppch is updated to point to next character after range.
- *
- * Exit-Failure
- * Prints error message and exits
- */
- void ParseRange(PRANGE prange, char **ppch)
- {
- BOOL fStartEnd; // TRUE => start-end, FALSE => start+count
- char *pchOriginal;
- char *pch;
-
- pchOriginal = *ppch;
- pch = pchOriginal+1; // Skip flag character
-
- prange->iFirst = ParseDecimal(&pch);
- if (prange->iFirst == dwRANGE_DEFAULT) { // No number present
- prange->iLast = dwRANGE_DEFAULT; // Last is default, too
- *ppch = pch; // Update parsing pointer
- return;
- }
-
- // Got First number, see if Last number or Count follows
-
- switch (*pch) {
- case chRANGE_START_END:
- fStartEnd = TRUE;
- break;
-
- case chRANGE_START_COUNT:
- fStartEnd = FALSE;
- break;
-
- default: // No second number, so set last == first
- prange->iLast = prange->iFirst;
- *ppch = pch; // Update parsing pointer
- return;
- }
-
- // Get Last number
-
- pch++; // Skip separator
- prange->iLast = ParseDecimal(&pch);
- if (prange->iLast == dwRANGE_DEFAULT) { // No number present
- pchOriginal[1] = '\0'; // Trim off all but switch character
- Error("Bad range specified for switch %s",pchOriginal);
- }
-
- if (fStartEnd) { // Make sure last >= first
- if (prange->iFirst > prange->iLast) {
- pchOriginal[1] = '\0'; // Trim off all but switch character
- Error("First > Last in range for switch %s",pchOriginal);
- }
- }
- else { // Set last to first+count-1
- prange->iLast += (prange->iFirst - 1);
- }
-
- // Update parsing pointer
- *ppch = pch;
- }
-
-
- /*** ParseDecimal - Parse decimal number
- *
- * Entry
- * ppch - pointer to pointer to potential number
- *
- * Exit-Success
- * Returns a DWORD.
- * *ppch adjusted to point immediately following parsed number.
- *
- * Exit-Failure
- * Returns dwRANGE_DEFAULT -- no number was present
- */
- DWORD ParseDecimal(char **ppch)
- {
- DWORD dw=0;
- char *pch=*ppch;
-
- if (!isdigit(*pch)) { // No number here
- return dwRANGE_DEFAULT;
- }
-
- while (isdigit(*pch)) {
- dw = dw*10 + (*pch - '0');
- pch++;
- }
-
- *ppch = pch; // Update command line pointer
- return dw; // Return parsed number
- }
-
-
- /*** ShowSyntax - Display command-line syntax
- *
- */
- void ShowSyntax(BOOL fFull)
- {
- int i;
- int cLines;
-
- if (fFull) { // Show full help
- cLines = sizeof(apszSyntax)/sizeof(char *);
- for (i=0; i<cLines; i++) {
- printf("%s\n",apszSyntax[i]);
- }
- }
- else { // Just show summary line
- printf("%s\n",apszSyntax[isynSUMMARY]);
- }
-
- exit(0);
- }
-
-
- /*** PrintBanner - print banner for this program
- *
- */
- void PrintBanner(void)
- {
- #ifndef SNAP
- printf("%s File Dumper - Version %d.%02d\n",szProduct,verMAJOR,verMINOR);
- #else
- printf("%s Snapper - Version %d.%02d\n",szProduct,verMAJOR,verMINOR);
- #endif
- }
-
- #ifdef SNAP // -----------------------------------------------------
-
- /*** SnapCVF - 'snap' CVF reserved info to file
- *
- * Entry
- * fhCVF - file handle of open CVF file
- */
-
- void SnapCVF(FILEHANDLE fhCVF)
- {
- FILEHANDLE fhOut;
- int iErr = 0;
- UINT iThisLen;
- UINT iOutLen;
- UINT iLen = 60 * 1024;
- long lCpyLen;
- char *pCpyBuf;
-
- // Open/create output file
-
- if (_dos_creat(g.achOutFileName, _A_NORMAL, &fhOut) != 0)
- Error("Could not create: %s", g.achOutFileName);
-
- // Allocate copy buffer
-
- while ( ((pCpyBuf = malloc(iLen)) == NULL) && (iLen > 4 * 1024) )
- iLen -= 4 * 1024;
-
- if (pCpyBuf == NULL)
- Error("Insufficient memory for copy buffer",NULL);
-
- // Tell user what we are doing
- if (g.chDrive != 0)
- sprintf(g.ach,"Drive %c: (mounted from %s)",g.chDrive,g.achCVFName);
- else
- strcpy(g.ach,g.achCVFName);
- printf("Writing snapshot of %s to %s\n",g.ach,g.achOutFileName);
-
- // Copy CVF up to and including root directory to out file
-
- lCpyLen = areg[iregSECTORHEAP].ibStart; // copy to start of sector heap
- lseek(fhCVF, 0L, SEEK_SET); // start at the begining
-
- while (lCpyLen > 0) {
- iThisLen = (lCpyLen > (long)iLen) ? iLen : (int)lCpyLen;
- if (FileRead(fhCVF,FR_CURRENT_POS,pCpyBuf,iThisLen) != 0)
- {
- iErr = 1;
- break;
- }
-
- if (_dos_write(fhOut, pCpyBuf, iThisLen, &iOutLen) != 0 ||
- iOutLen != iThisLen)
- {
- iErr = 2;
- break;
- }
-
- lCpyLen -= iThisLen;
- }
-
- // Clean up and exit
-
- free(pCpyBuf);
-
- _dos_close(fhOut);
-
- if (iErr)
- Error((iErr==1) ? "Error while reading CVF" :
- "Error while writing snap file", NULL);
- }
-
- #endif // SNAP --------------------------------------------------------
-
-
- /*** ComputeRegions - Compute regions of CVF, from MDBPB
- *
- * NOTE: This is the most interesting part of the program!
- *
- * Entry
- * fh - File handle of CVF
- * g.mp - Filled in with MD BPB from CVF already
- *
- * Exit-Success
- * areg filled in.
- *
- * Exit-Failure
- * Prints error message and exits.
- */
- void ComputeRegions(FILEHANDLE fh)
- {
- int ireg; // Index to walk region table
- int cbPerSec; // Count of bytes per sector
- char csecPerClu; // Count of sectors per cluster
- long ccluTotal; // Current total clusters
- long ccluTotalMax; // Maximum total clusters
- long csecTotal; // Current total sectors
- long csecTotalMax; // Maximum total sectors
- long seekpos; // File seek position
-
- // Get common values, to make code more readable
-
- cbPerSec = g.mp.cbPerSec; // Count of bytes per sector
- csecPerClu = g.mp.csecPerClu; // Count of sectors per cluster
-
- // Get drive size reported to DOS when CVF is mounted
-
- if (g.mp.csecTotalWORD != 0) // Small drive
- csecTotal = g.mp.csecTotalWORD;
- else // Large drive
- csecTotal = g.mp.csecTotalDWORD;
- ccluTotal = csecTotal/csecPerClu; // Total number of clusters
-
- // Check CVF signatures
-
- seekpos = (g.mp.csecMDReserved + 1) * (long)cbPerSec;
- CheckSignature(fh, seekpos, "first");
-
- if ((g.cbCVF = lseek(fh,0L,SEEK_END)) == -1L)
- Error("Could not seek to end of CVF",NULL);
-
- // The 2nd stamp is located at the start of the last complete sector
- // in the CVF. If the CVF is exactly a sector multiple, then this
- // is indeed the last sector of the file. However, sometimes CVFs are
- // not exactly a sector multiple in length, in which case it is the
- // next to last sector of the CVF which contains the 2nd stamp.
-
- g.ibCVFStamp2 = (g.cbCVF/cbPerSec - csecRETRACT_STAMP) * cbPerSec;
- CheckSignature(fh, g.ibCVFStamp2, "second");
-
- // Get Maximum CVF size information
-
- csecTotalMax = (g.mp.cmbCVFMax * 1024L * 1024L) / cbPerSec;
- ccluTotalMax = csecTotalMax / csecPerClu;
-
- // Compute MDBPB region
-
- ireg = iregMDBPB;
- areg[ireg].ibStart = 0L; // Always first thing in the CVF
- areg[ireg].cbTotal = cbPerSec; // Always consumes one sector
- areg[ireg].cbActive = sizeof(MDBPB); // Only MDBPB structure is valid
-
- // Compute BitFAT region
-
- ireg++; // iregBITFAT
- areg[ireg].ibStart = areg[ireg-1].ibStart + areg[ireg-1].cbTotal;
-
- // The BitFAT cbTotal should also ==
- // (cmbCVFMax * 1024 * 1024) / (8 * cbPerSec)
- // (file capicity in sectors / 8 sector bits per byte)
-
- areg[ireg].cbTotal = g.mp.cpageBitFAT * (long)cbPER_BITFAT_PAGE;
- // areg[iregBIITFAT].cbActive is computed below, after we know how large
- // the sector heap is.
-
- // Compute RESERVED1 region
-
- ireg++; // iregRESERVED1
- areg[ireg].ibStart = areg[iregBITFAT].ibStart + areg[iregBITFAT].cbTotal;
- areg[ireg].cbTotal = csecRESERVED1 * (long)cbPerSec;
- areg[ireg].cbActive = 0; // none in use
-
- // Compute MDFAT region
-
- ireg++; // iregMDFAT
-
- // The MDFAT starts just after the BitFAT so
- // secMDFATStart * cbPerSec + cbPerSec (for MDBPB) should ==
- // areg[iregBITFAT].ibStart + areg[iregBITFAT].cbTotal
-
- areg[ireg].ibStart = g.mp.secMDFATStart * (long)cbPerSec + (long)cbPerSec;
-
- // The MDFAT size depends on the maximum number of clusters that the CVF
- // could hold, but we will compute it instead by inference from the
- // other information we have, so we'll calculate the MDFAT size as
- // MDReserved - BitFAT size - MDBPB size - other reserved sizes
-
- areg[ireg].cbTotal = (g.mp.csecMDReserved * (long)cbPerSec) -
- areg[iregMDBPB].cbTotal - // MDBPB size
- areg[iregBITFAT].cbTotal - // BitFAT size
- areg[iregRESERVED1].cbTotal - // RESERVED1 size
- (csecRESERVED2 * (long)cbPerSec); // RESERVED2 size
- areg[ireg].cbActive = ccluTotal * cbMDFATENTRY;
-
- // Compute RESERVED2 region
-
- ireg++; // iregRESERVED2
- areg[ireg].ibStart = areg[iregMDFAT].ibStart + areg[iregMDFAT].cbTotal;
- areg[ireg].cbTotal = csecRESERVED2 * (long)cbPerSec;
- areg[ireg].cbActive = 0; // none in use
-
- // Compute BOOT region
-
- ireg++; // iregDOSBOOT
- areg[ireg].ibStart = g.mp.csecMDReserved * (long)cbPerSec;
- areg[ireg].cbTotal = cbPerSec;
- areg[ireg].cbActive = cbPerSec;
-
- // Compute RESERVED3 region
-
- ireg++; // iregRESERVED3
- areg[ireg].ibStart = areg[iregDOSBOOT].ibStart
- + areg[iregDOSBOOT].cbTotal;
- areg[ireg].cbTotal = (g.mp.csecReserved - 1) * (long)cbPerSec;
- areg[ireg].cbActive = 0;
-
- // Compute DOSFAT region
-
- ireg++; // iregDOSFAT
- areg[ireg].ibStart = areg[iregDOSBOOT].ibStart +
- (g.mp.csecReserved * (long)cbPerSec);
- areg[ireg].cbTotal = g.mp.csecFAT * (long)cbPerSec;
- areg[ireg].cbActive = g.mp.f12BitFAT ?
- ((ccluTotal * 3)/2) : // 12-bit FAT
- (ccluTotal * 2); // 16-bit FAT
-
- // Compute ROOTDIR region
-
- ireg++; // iregDOSROOTDIR
- areg[ireg].ibStart = (g.mp.secRootDirStart + g.mp.csecMDReserved)
- * (long)cbPerSec;
- areg[ireg].cbTotal = g.mp.cRootDirEntries * cbDIR_ENT;
- areg[ireg].cbActive = areg[ireg].cbTotal;
-
- // Compute RESERVED4 region
-
- ireg++; // iregRESERVED4
- areg[ireg].ibStart = areg[iregDOSROOTDIR].ibStart
- + areg[iregDOSROOTDIR].cbTotal;
- areg[ireg].cbTotal = csecRESERVED4 * (long)cbPerSec;
- areg[ireg].cbActive = 0;
-
- // Compute SECTORHEAP region
-
- ireg++; // iregSECTORHEAP
- areg[ireg].ibStart = areg[iregRESERVED4].ibStart
- + areg[iregRESERVED4].cbTotal;
-
- // Total and Active SECTORHEAP sizes are the same -- unlike other
- // regions, the SECTORHEAP is not preallocated for the max capacity.
- // The SECTORHEAP is followed by the 2nd MD STAMP, which occupies
- // the last <2 sectors of the CVF. Since we already did the
- // RETRACT_STAMP computation above, all we have to do is subtract
- // the start of the sector heap from the start of the 2nd stamp.
-
- areg[ireg].cbTotal = g.ibCVFStamp2 - areg[ireg].ibStart;
- areg[ireg].cbActive = areg[ireg].cbTotal;
-
- // Now we can compute the active region of the BitFAT. There is
- // one bit in the BitFAT for every sector in the sector heap, and
- // we round up to the nearest byte.
- areg[iregBITFAT].cbActive = (areg[iregSECTORHEAP].cbTotal/cbPerSec + 7) / 8;
-
- #ifndef SNAP
- // Compute valid ranges
-
- g.rangeCVFValid.iFirst = 0;
- g.rangeCVFValid.iLast = (g.cbCVF+cbPerSec-1)/cbPerSec;
-
- // Heap sector numbers are one less than their CVF position
- g.rangeHeapValid.iFirst = areg[iregSECTORHEAP].ibStart/cbPerSec - 1;
- g.rangeHeapValid.iLast = g.rangeHeapValid.iFirst
- + areg[iregSECTORHEAP].cbTotal/cbPerSec
- - 1;
-
- g.rangeMDFATValid.iFirst = 2 + g.mp.cluFirstData;
- g.rangeMDFATValid.iLast = g.rangeMDFATValid.iFirst
- + areg[iregMDFAT].cbActive/cbMDFATENTRY
- - 1;
-
- g.rangeBitFATValid.iFirst = g.rangeHeapValid.iFirst;
- g.rangeBitFATValid.iLast = g.rangeHeapValid.iLast;
- #endif
- }
-
-
- /*** CheckSignature - Check a CVF signature
- *
- * Entry
- * fh - File handle of CVF
- * seekpos - Position to check
- * pszName - Signature name
- *
- * Exit-Success
- * Returns.
- *
- * Exit-Success
- * Prints error;
- * if g.fIgnoreSigCheck is set, returns
- * else Exits program
- */
- void CheckSignature(FILEHANDLE fh, long seekpos, char *pszName)
- {
- char achStamp[cbDS_STAMP]; // Buffer to read stamp
-
- if (FileRead(fh,seekpos,achStamp,sizeof(achStamp)) != 0) {
- sprintf(g.ach,"Could not read %s signature: %s",pszName,g.achCVFName);
- goto Error;
- }
- if ( (strcmp(achStamp,szDS_STAMP1) != 0) &&
- (strcmp(achStamp,szDS_STAMP2) != 0) ) {
- sprintf(g.ach,"Bad %s signature: %s",pszName,g.achCVFName);
- goto Error;
- }
- return; // Signature is okay
-
- Error: // Signature is bad
- if (g.fIgnoreSigCheck)
- printf("%s\n",g.ach);
- else
- Error("%s",g.ach);
- }
-
-
- /*** BuildCVFName - Parse CVF abbreviations into full file name
- *
- * Entry
- * psz - possible CVF abbreviation
- * <empty> => Full path of CVF for current drive
- * d: => Full path of CVF for specified drive
- * NOTE: If drive is not a DS drive, assume it is
- * a host drive, and take 000.
- * [path]file => Assume it is a CVF file name
- *
- * ach - buffer to receive full name
- * cb - length of ach (should be >80 bytes, for error message!)
- * pchDrive - receives drive letter of DS drive (if specified)
- *
- * Exit-Success
- * returns TRUE
- * ach filled in with fully-qualified CVF path;
- *
- * Exit-Failure
- * returns FALSE
- * ach filled in with error message
- */
- BOOL BuildCVFName(char *psz, char ach[], int cb, char *pchDrive)
- {
- SEQ seq;
- int dr;
- BYTE drHost;
- BOOL fSwapped;
-
- if ( (psz == NULL) || (*psz == '\0') ) { // <empty>
- _dos_getdrive(&dr); // Get current drive number (1-based)
- dr--; // Make drive zero-based
- }
- else if (IsDriveSpec(psz)) { // Could be just drive letter
- dr = (char)toupper((int)psz[0])-'A'; // Zero-based drive number
- }
- else { // Allow any file name
- strcpy(ach,psz);
- *pchDrive = 0; // Drive not specified
- return TRUE;
- }
-
- // Build CVF path from host drive and sequence number
-
- if (IsDoubleSpaceDrive((BYTE)dr,&fSwapped,&drHost,&seq)) {
- *pchDrive = (char)(dr+'A'); // Mounted drive letter
- sprintf(ach,"%c:\\%s.%03d",(int)drHost+'A',szCVF_ROOT,seq);
- return TRUE;
- }
- else {
- sprintf(ach,"Not a DoubleSpace drive: %c",dr+'A');
- return FALSE;
- }
- }
-
-
- /*** IsDriveSpec - checks if parameter is a valid drive specifier
- *
- * Entry
- * psz - string to check; valid forms are:
- * "a" or "a:", where a is in [a..zA..Z]
- *
- * Exit-Success
- * Returns TRUE.
- *
- * Exit-Failure
- * Returns FALSE.
- */
- BOOL IsDriveSpec(char *psz)
- {
- int cb;
-
- if (psz == NULL)
- return FALSE;
-
- cb = strlen(psz);
- if (cb > 2)
- return FALSE;
-
- if (!isalpha(*psz))
- return FALSE;
-
- if ( (cb == 2) && (psz[1] != ':') )
- return FALSE;
-
- return TRUE;
- }
-